package com.interviews.atomic;

import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.LongAccumulator;
import java.util.stream.IntStream;

/**
 * Accumulator 和 Adder 非常相似，实际上 Accumulator 就是一个更通用版本的 Adder，比如 LongAccumulator 是 LongAdder 的功能增强版，
 * 因为 LongAdder 的 API 只有对数值的加减，而 LongAccumulator 提供了自定义的函数操作
 *
 * @author qian
 * @version 1.0
 * @date 2022/3/3 10:59
 */
public class AccumulatorDemo {

    /**
     * 首先新建了一个 LongAccumulator，同时给它传入了两个参数；
     * 然后又新建了一个 8 线程的线程池，并且利用整形流也就是 IntStream 往线程池中提交了从 1 ~ 9 这 9 个任务；
     * 之后等待了两秒钟，这两秒钟的作用是等待线程池的任务执行完毕；
     * 最后把 accumulator 的值打印出来。
     *
     * LongAccumulator result = new LongAccumulator((x, y) -> x * y, 0);
     * LongAccumulator min = new LongAccumulator((x, y) -> Math.min(x, y), 0);
     * LongAccumulator max = new LongAccumulator((x, y) -> Math.max(x, y), 0);
     *
     * LongAccumulator 的一大优势就是可以利用线程池来为它工作。一旦使用了线程池，那么多个线程之间是可以并行计算的，
     * 效率要比之前的串行高得多。这也是为什么刚才说它加的顺序是不固定的，因为我们并不能保证各个线程之间的执行顺序，所
     * 能保证的就是最终的结果是确定的。
     *
     */
    public static void main(String[] args) throws InterruptedException {
        LongAccumulator accumulator =  new LongAccumulator(Long::sum, 0);
        ExecutorService executorService = new ThreadPoolExecutor(8, 8, 0L,TimeUnit.MILLISECONDS, new LinkedBlockingDeque<>(),
        new AccumulatorThreadFactory("accumulator"),new ThreadPoolExecutor.AbortPolicy());
        IntStream.range(1, 10).forEach(i -> executorService.execute(() -> accumulator.accumulate(i)));
        Thread.sleep(2000);
        System.out.println(accumulator.getThenReset());
    }

    static class AccumulatorThreadFactory implements ThreadFactory{

        private final String namePrefix;
        private final AtomicInteger nextId = new AtomicInteger(1);

        public AccumulatorThreadFactory(String namePrefix) {
            this.namePrefix = "From AccumulatorThreadFactory "+namePrefix+"-Worker-";
        }

        @Override
        public Thread newThread(Runnable r) {
            String name = namePrefix+nextId.getAndIncrement();
            Thread thread = new Thread(null,r,name,0);
            System.out.println(thread.getName());
            return thread;
        }
    }
}
